// -*- C++ -*-
//
//  This file is part of Siena, a wide-area event notification system.
//  See http://www.inf.usi.ch/carzaniga/siena/
//
//  Authors: Antonio Carzaniga
//  See the file AUTHORS for full details. 
//
//  Copyright (C) 2003 University of Colorado
//  Copyright (C) 2005 Antonio Carzaniga
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 2
//  of the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
//  USA, or send email to one of the authors.
//
//
// $Id: b_table.cc,v 1.21 2010-03-11 15:16:38 carzanig Exp $
//
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string>

#ifdef DEBUG_OUTPUT
#include <iostream>
#define DBG(x) {std::cout << x ;}
#else
#define DBG(x) 
#endif

#include "siena/forwarding.h"
#include "siena/allocator.h"
#include "siena/btable.h"

#include "bset_encoding.h"
#include "b_predicate.h"
#include "mmhwrapper.h"

#ifdef WITH_STATISTICS
#include "statistics.h"
#endif

namespace siena {

/** @brief a predicate in a BTable.
 *
 *  This is an extremely simple data structure.  The predicate is
 *  represented as a linked list of filters.  Then, each predicate
 *  refers directly to its interface id, and is itself a link in a
 *  linked list of predicates, which is what makes up the BTable.
 **/
struct BPredicate {
    BPredicate * next;
    ifid_t id;
    BFilter * flist;

    BPredicate(BPredicate *n, ifid_t i, BFilter * fl) 
	: next(n), id(i), flist(fl) {}
};

BTable::BTable() throw() : plist(0) {}
BTable::~BTable() throw() {}
SortedBTable::SortedBTable() throw() : BTable() {}

void BTable::ifconfig(if_t id, const predicate & p) throw(bad_constraint) {
    //    cons_timer.push ();
    BFilter * flist = encode_predicate<BFilter>(p, memory);
    if (flist) {
	plist = new (memory)BPredicate(plist, id, flist);
    }
    //    cons_timer.pop ();
}

void BTable::match(const message & m, MatchHandler & h) const throw() {
    DBG("match: " << std::endl);
    BPredicate * p = plist;
    if (!p) return;

#ifdef WITH_STATISTICS
    sff_timer_stack.push(bloom_encoding_timer);
#endif
    bset_t b;
    encode_message(b, &m);
    DBG("> " <<  b << std::endl);
    
#ifdef WITH_STATISTICS
    sff_timer_stack.pop();
    sff_timer_stack.push(match_timer);
#endif

    do {
	for(BFilter * f = p->flist; f != 0; f = f->next) {
	    DBG("? " << f->b << std::endl);
	    if (b.covers(f->b)) {
		DBG("! " << p->id << std::endl);

#ifdef WITH_STATISTICS
		sff_timer_stack.push(forward_timer);
#endif
		bool output_result = h.output(p->id);
#ifdef WITH_STATISTICS
		sff_timer_stack.pop();
#endif
		if (output_result) {
#ifdef WITH_STATISTICS
		    sff_timer_stack.pop();
#else
		    return;
#endif
		}
		break;
	    }
	}
    } while ((p = p->next));

#ifdef WITH_STATISTICS
    sff_timer_stack.pop();
#endif
}

void BTable::match(const message & m, MatchMessageHandler & p) const throw() {
    MMHWrapper w(p, m);
    match(m, w);
}

void BTable::clear() throw() {
    memory.clear();
    plist = 0;
}
void BTable::clear_recycle() throw() {
    memory.recycle();
    plist = 0;
}

size_t BTable::allocated_bytesize() const throw() {
    return memory.allocated_size();
}

size_t BTable::bytesize() const throw() {
    return memory.size();
}

static void sort_flist(struct BPredicate * p) {
    // this is plain and simple insertion sort.  We build a new
    // (sorted) list of filters starting with new_head
    BFilter * head = p->flist;
    BFilter * new_head = 0;

    while (head) {
	BFilter ** curr = &new_head; 
	while ((*curr) && head->b.count() > (*curr)->b.count())
	    curr = &((*curr)->next);
	BFilter * tmp = head->next;
	head->next = *curr;
	*curr = head;
	head = tmp;
    }
    p->flist = new_head;
}

void SortedBTable::consolidate() throw() {
#ifdef WITH_STATISTICS
    sff_timer_stack.push(consolidate_timer);
#endif
    for(BPredicate * p = plist; (p) ; p = p->next)
	sort_flist(p);
#ifdef WITH_STATISTICS
    sff_timer_stack.pop();
#endif
}

} // end namespace siena
